/**
 *  基本弹出层
 *  $("#container").modal(options);
 *  modal.open({
 *      container: "#container"
 *  });
 *  
 *  弹出消息
 *  modal.message("test message");
 *  modal.message({
 *      content: "test message",
 *      ...
 *  });
 *  
 *  loading弹窗
 *  modal.loading();
 *  setTimeout(function(){
 *      modal.close(function(){
 *          ...
 *      });
 *  }, 2000);
 *  
 *  关闭弹窗
 *  modal.close();
 */

var modal = (function () {

    //弹出层ID数字
    var _modalID = 0;
    //setTimeout ID
    var _setTimeOutID = 0;
    //遮罩层元素ID
    var shadeID = "modal-shade";
    //需要移除的元素标识
    var needRemoveClass = "modal-need-remove";
    //共享设置参数
    var options = {};

    var device = {
        iOS: /iphone|ipad/i.test(navigator.userAgent),
        Android: /android/i.test(navigator.userAgent),
        WeChat: /micromessenger/i.test(navigator.userAgent)
    };

    var vendors = ["-webkit-", "-ms-"];

    function optionsConstructor(opts) {

        var defaults = {
            container: null,
            content: null,
            duration: 2000,
            hasShade: true,
            removeShade: true,
            backdropClose: true,
            hasClose: false,
            autoClose: false,
            needRemove: false,
            onclose: null,
            noStyle: false,
            animation: true,
            opacity: 0.6,
            open: {
                duration: 400,
                complete: null
            },
            close: {
                duration: 150
            }
        };

        $.extend(defaults, opts);

        //设置开启或关闭动画效果
        if (!defaults.animation) defaults.open.duration = defaults.close.duration = 0;

        return defaults;
    }

    /**
     * 创建新弹出层元素ID
     * @returns {string}
     * @private
     */
    var _createModalID = function(){
        return "modal-auto" + (++_modalID);
    };

    /**
     * 创建弹出层容器
     * @returns {*|jQuery|HTMLElement}
     * @private
     */
    var _createModalWrap = function(){
        var $wrap = $('<div id="'+ _createModalID() +'" class="modal-wrapper" style="display: none;">' +
            '   <div class="modal-container">' +
            '       <div class="modal-body"></div>' +
            '   </div>' +
            '</div>');
        if (options.isLoading) {
            $wrap.find(".modal-body").remove();
        }
        _addRemoveClass($wrap);
        return $wrap;
    };

    /**
     * 获取当前弹出层元素ID
     * @returns {string}
     * @private
     */
    var _getModalID = function(){
        return "modal-auto" + _modalID;
    };

    /**
     * 获取当前弹出层元素
     * @returns {*|jQuery|HTMLElement}
     * @private
     */
    var _getModalEl = function(){
        var $modal;
        if (options.container != null){
            $modal = options.IsWrapped ? $(options.container).closest(".modal-wrapper") : $modal = $(options.container);
        }else{
            $modal = $("#" + _getModalID())
        }
        return $modal;
    };

    /**
     * 获取当前弹出层内容容器
     * @param $modal
     * @returns {*}
     * @private
     */
    var _getContentEl = function () {
        var $modal = _getModalEl();
        var $content = $modal.find(".modal-body");
        if ($content.length == 0) {
            $content = $modal.find(".modal-container");
        }
        return $content;
    };

    var _appendStyle = function(){
        var $head = $(document.head);
        if ($("#modal-style").length == 0){
            //$head.prepend(
            //    '<style id="modal-style">' +
            //    '.modal-wrapper{top:0; left:0; right:0; bottom:0; position:fixed; margin:auto; z-index:9999; width:100%;' +
            //            'box-sizing:border-box; text-align:center; max-height:100%; overflow-y:auto; -webkit-overflow-scrolling:touch;}' +
            //    '.modal-container{display:inline-block; position:relative; margin: 0 auto; text-align:initial; vertical-align:middle;}' +
            //    '.modal-body{padding:20px;}' +
            //    '.modal-close{color:inherit; font-size:30px; position:absolute; top:-30px; right:0; width:30px; height:30px; line-height:30px; cursor:pointer;}' +
            //    '.modal-shade{background-color:#000; opacity:0.6; top:-200%; bottom:0; left:-200%; right:0; height:500%; width:500%; position:fixed; z-index:9998;}' +
            //    '</style>'
            //);
        }
    };

    var _createCloseButton = function($modal){
        var $close = $('<div class="modal-close">×</div>');
        $modal.find(".modal-container").prepend($close);
        $close.click(function(){
            modal.close({
                container: $modal
            });
        });
    };

    var _wrapElement = function($el){
        $el.wrap(
                '<div id="'+ _createModalID() +'" class="modal-wrapper" style="display:none;">' +
                '<div class="modal-container">' +
                '<div class="modal-body">'
        ).show();
        var $modal = _getModalEl();
        _addRemoveClass($modal);
        $modal.parents(":hidden").show().attr("data-sign", "modal-show");
    };

    var _unwrapElement = function($el){
        $el.unwrap("<div class='modal-body'>")
            .unwrap("<div class='modal-container'>")
            .unwrap("<div class='modal-wrapper'>");
        $("[data-sign='modal-show']").hide();
    };
    
    var _addRemoveClass = function($modal){
        if (options.needRemove) $modal.addClass(needRemoveClass)
    };

    /**
     * 创建弹出层结构
     * @returns {*}
     * @private
     */
    var _createModal = function(){

        var $modal;

        _appendStyle();

        if (options.container){
            var $container = $(options.container);
            if (!$container.hasClass("modal-wrapper") && $container.find(".modal-wrapper").length < 1){
                _wrapElement($container);
                options.IsWrapped = true;
            }
            $modal = _getModalEl();
        }else{
            $modal = _createModalWrap();
            $(document.body).append($modal);
            _getContentEl().html(options.content);
        }

        var rect = getComputedRect($modal.find(".modal-container"));
        $modal.css({
            width: rect.width,
            height: rect.height
        });

        return $modal;
    };

    /**
     * 添加遮罩层元素
     * @private
     */
    var _appendShade = function (opts) {
        $('<div id="' + shadeID + '" class="modal-shade"></div>').insertBefore(_getModalEl());
    };

    var _getShade = function () {
        return $("#" + shadeID);
    };

    /**
     * 移除遮罩层元素
     * @private
     */
    var _removeShade = function(){
        _getShade().remove();
    };

    /**
     * 获取隐藏元素真实高度
     * @param $el                   最外层被隐藏的容器
     * @param childrenSelector      要选择的子元素的选择器，可不传，不传则默认获取$el的高度
     * @returns {*}
     * @private
     */
    var _getHeight = function (element) {
        //var height, $target, $element = $(element);
        //$target = childrenSelector ? $element.find(childrenSelector) : $element;
        //addHidden($element);
        ////height = $target.height();
        //height = $target[0].getBoundingClientRect().height;
        //if (height < 1) {
        //    height = _getNumber(getComputedStyle($target[0]).height) +
        //             _getNumber($target.css("margin-top")) +
        //             _getNumber($target.css("margin-bottom")) +
        //             _getNumber($target.css("padding-top")) +
        //             _getNumber($target.css("padding-bottom"));
        //}
        //removeHidden($element);
        //return height;
        return getComputedRect($(element)).height;
    };

    function getHiddenParents(element){
        var hiddenParents = [];
        $(element).parents().each(function(){
            if ($(this).css("display") == "none"){
                hiddenParents.push(this);
            }
        });
        return hiddenParents;
    }

    function getComputedRect(element, selector){
        var rect = null;
        var targetElement = selector ? $(element).find(selector)[0] : $(element)[0];
        if (targetElement){
            renderHiddenElement(element, function(){
                if (targetElement.getBoundingClientRect){
                    rect = targetElement.getBoundingClientRect();
                } else {
                    rect = {
                        width: $(targetElement).width(),
                        height: $(targetElement).height(),
                        top: targetElement.offsetTop,
                        left: targetElement.offsetLeft
                    };
                    rect.right = rect.left + rect.width;
                    rect.bottom = rect.top + rect.height;
                }
            });
        }
        return rect;
    }

    function renderHiddenElement(element, callback){
        var $element = $(element);
        var hiddenParents = getHiddenParents($element);
        hiddenParents.push($element);
        hiddenParents.forEach(function(item, index, obj){
            $(item).css({
                "visibility": "visible",
                "position": "absolute",
                "display": "block"
            });
        });
        callback && callback.call($element[0]);
        hiddenParents.forEach(function(item, index, obj){
            $(item).css({
                "visibility": "",
                "position": "",
                "display": ""
            });
        });
    }

    var _getNumber = function (str) {
        try {
            return parseInt(/\d+/.exec(str)[0]);
        } catch (e) {
            console.log(e);
            return null;
        }
    };

    function animation(element, animationName, duration, complete) {

        var $element = $(element);

        setPrefixAttr($element, "animation-duration", duration + "ms");

        $element.one("webkitAnimationEnd animationend", function () {
            //$(this).off("webkitAnimationEnd animationend");
            complete && complete();
        });

        setPrefixAttr($element, "animation-name", animationName);
    }

    function setPrefixAttr(element, attributes, value) {
        if (typeof (attributes) == "string") {
            vendors.forEach(function (prefix, idx, obj) {
                $(element).css(prefix + attributes, value);
            });
        } else {
            for (var key in attributes) {
                vendors.forEach(function (prefix, idx, obj) {
                    $(element).css(prefix + key, attributes[key]);
                });
            }
        }
    }

    /**
     * 打开弹出层
     * @param userOptions
     * @returns {*}
     * @private
     */
    var _status = 0;

    var _open = function (userOptions) {

        _status = 1;

        if ($(".modal-wrapper:visible").length > 0) {
            return;
        }

        //初始化设置参数
        options = optionsConstructor(userOptions);

		//添加弹出层dom
        var $modal = _createModal();

        //触发窗体打开前事件
        $modal.trigger("modal.opening");

        //添加遮罩层
        options.hasShade && _appendShade(options);

        if (options.animation) {
            animation($modal, "bounceIn", 400, function () {
                //触发窗体已打开事件
                $modal.trigger("modal.opened");
                options.open.complete && options.open.complete();
                _status = 2;
            });
        } else {
            //触发窗体已打开事件
            $modal.trigger("modal.opened");
            options.open.complete && options.open.complete();
            _status = 2;
        }
        $modal.show();

        //设置自动关闭
        if (options.autoClose) {
            options.autoClose && _timeoutClose(userOptions);
        } else if (options.backdropClose) {
            setTimeout(function () {
                _getShade().attr("data-dismiss", "modal");
            }, 600);
        }

        return $modal;
    };

    /**
     * 关闭弹出层
     * @param userOptions
     * @private
     */
    var _close = function (userOptions) {

        if ((!options.container && !options.content) || _status < 2) return;

        if (typeof (userOptions) == "function") {
            options.onclose = userOptions;
        } else {
            options = $.extend(options, userOptions);
        }

        //获取当前弹出层
        var $modal, $el;
        $modal = options.container != null ? $(options.container) : _getModalEl();

        //触发窗体关闭前事件
        $modal.trigger("modal.closing");

        if (options.IsWrapped){
            $el = $modal;
            $modal = $el.parents(".modal-wrapper");
        }

        if (options.animation) {
            animation($modal, "bounceOut", 200, function () { closeAnimationComplete(options) });
        } else {
            closeAnimationComplete(options)
        }

        function closeAnimationComplete(options) {
            //移除遮罩层
            options.removeShade && _removeShade();
            //隐藏元素
            $modal.hide();
            //如果元素是被wrap的，则在关闭之后要unwrap
            $(".modal-need-remove").remove();
            if (options.IsWrapped) {
                _unwrapElement($el);
            }
            //执行onclose方法
            options.onclose && options.onclose($modal);
            //触发窗体已关闭事件
            $modal.trigger("modal.closed");

            _status = 0;
        }
    };

    /**
     * 弹出层消息
     * @param userOptions
     * @private
     */
    var _message = function(userOptions, extOptions){
        //初始化参数
        if (typeof(userOptions) !== "object"){
            userOptions = $.extend({ content: userOptions }, extOptions);
        }
        //userOptions = typeof(userOptions) == "string" ? {content:userOptions} : userOptions;
        //消息方法不能传递容器
        if (userOptions.container){
            console.log("message方法不能传递container，请使用open");
            return;
        }
        //message方法默认自动关闭
        userOptions.autoClose = typeof(userOptions.autoClose) == "undefined" ? true : userOptions.autoClose;
        //打开弹出层
        var $modal = _open(userOptions);
        //设置关闭事件，用于移除弹出层dom
        var userClose = userOptions.onclose;
        userOptions.onclose = function(){
            userClose && userClose($modal);
            $modal.remove();
        }
    };

    //超时关闭
    var _timeoutClose = function(userOptions){
        clearTimeout(_setTimeOutID);
        _setTimeOutID = setTimeout(function(){
            _close(userOptions);
        }, options.duration);
    };

    var _loading = function (userOptions) {
        var loadingHtml = '<div class="loading-wrap"><div class="loading-box loading-animate">';
        for (var i=0; i<6; i++){
            loadingHtml += '<div class="loading-block"></div>';
        }
        loadingHtml += '</div></div>';

        var loadingOptions = {
            content: loadingHtml,
            autoClose: false,
            noStyle: true,
            animation: false,
            needRemove: true,
            isLoading: true,
            backdropClose: false
        };
        _open($.extend(loadingOptions, userOptions));
    };

    var _confirm = function (message, confirm) {
        var $btnConfirm = $('<button type="button" class="btn btn-sm">确定</button>').click(confirm);
        var $htmlCode = $('<div id=' + _createModalID() + ' class="modal-wrapper ' + needRemoveClass + '">'+
                           '<div class="modal-container modal-lg modal-confirm">'+
                               '<div class="modal-content">'+
                                   '<div class="modal-body">'+
                                       '<div class="message">' + message + '</div>' +
                                   '</div>'+
                                   '<div class="modal-foot">'+
                                       '<button type="button" class="btn btn-sm" data-dismiss="modal">取消</button>'+
                                   '</div>'+
                               '</div>'+
                           '</div>' +
                       '</div>');
        $htmlCode.find(".modal-foot").prepend($btnConfirm);
        $(document.body).append($htmlCode);
        var options = {
            container: $htmlCode,
            needRemove: true
        };
        _open(options);
    };

    return {
        open: _open,
        close: _close,
        message: _message,
        confirm: _confirm,
        defaults: optionsConstructor,
        loading: _loading,
        removeShade: _removeShade
    }
})();

$(function () {
    $.fn.modal = function (userOptions) {
        if ($(this).length == 0) return;
        if (!userOptions) userOptions = {};
        userOptions.container = this;
        modal.open(userOptions);
    };
    $(document).on("click", "[data-modal]", function () {
        $($(this).attr("data-modal")).modal();
    });
    $(document).on("click", "[data-modal-message]", function () {
        modal.message($(this).attr("data-modal-message"));
    });
    $(document).on("click", "[data-dismiss~='modal']", function () {
        modal.close();
    });
})